<?php
/**
 * +-------------------------------------------------------------
 * |Caption:
 * +-------------------------------------------------------------
 * |Author: Lenny
 * +-------------------------------------------------------------
 * |Email:  1685964109@qq.com
 * +-------------------------------------------------------------
 * |Date:   2024/7/30 下午2:50
 * +-------------------------------------------------------------
 */

namespace app\common\service\easysms;

use Overtrue\EasySms\EasySms;
use Overtrue\EasySms\Exceptions\InvalidArgumentException;
use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
use think\facade\Cache;

class EasySmsService
{
    public static ?EasySmsService $instance = null;
    // 短信配置信息
    private mixed $smsConfig;
    //
    private string $smsCode;

    // 最大发送次数，默认10次
    private int $sendTimes = 10;
    // 发送限制间隔时间，默认24小时
    private int $safeTime = 86400;

    public static function getInstance(): ?EasySmsService
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function __construct()
    {
        $this->smsConfig = sysConfig('sms');
        $this->smsCode   = mt_rand(100000, 999999);
    }

    /**
     * 发送短信(短信验证码场景)
     * 其他场景请自行扩展（如发送通知短信）
     * 参考扩展地址：vendor/overtrue/easy-sms/README.md
     * use Overtrue\EasySms
     * @param $phone
     * @return string
     */
    public function sendSms($phone): string
    {
        $easySmsConfig = EasySmsConfig::getConfig($this->smsConfig);
        $easySms       = new EasySms($easySmsConfig);
        // 发送短信
        try {
            // 验证手机号发送记录
            $this->smsRecord($phone);
            // 发送短信
            $easySms->send($phone, [
                    'template' => function ($gateway)
                    {
                        if ($gateway->getName() == 'aliyun') {
                            return $this->smsConfig['alsms_template_code'];
                        }
                        return 'SMS_xxxxx';//其他服务商的模板code
                    },
                    'data'     => ['code' => $this->smsCode]
            ]);
            cache($phone, $this->smsCode, 300); // 缓存5分钟
        } catch (InvalidArgumentException|NoGatewayAvailableException $e) {
            return $e->getMessage();
        }
        return '发送成功';
    }

    /**
     * 记录短信验证码发送记录并判断是否超出发送限制
     * @param $phone
     * @return void
     * @throws InvalidArgumentException
     */
    private function smsRecord($phone): void
    {
        // 获取发送记录缓存
        $record = Cache::get("sendCaptchaSMS.$phone");
        // 写入缓存:记录剩余发送次数
        if (empty($record)) {
            Cache::set("sendCaptchaSMS.$phone", ['times' => $this->sendTimes - 1], $this->safeTime);
            return;
        }
        // 判断发送次数是否合法
        if ($record['times'] <= 0) {
            throw new InvalidArgumentException('很抱歉，已超出今日最大发送次数限制');
        }
        // 发送次数递减
        Cache::set("sendCaptchaSMS.$phone", ['times' => $record['times'] - 1]);
    }
}